package org.clickframes.clipr.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarFile;

import javax.xml.bind.JAXBException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.clickframes.InternalLinkResolutionException;
import org.clickframes.clipr.EndUserException;
import org.clickframes.clipr.domain.App;
import org.clickframes.engine.manifest.RuntimeManifestGenerator;
import org.clickframes.model.Appspec;
import org.clickframes.model.AppspecConstraintViolationException;
import org.clickframes.model.PageNotFoundException;
import org.clickframes.techspec.Plugin;
import org.clickframes.techspec.Techspec;
import org.clickframes.techspec.TechspecRunner;
import org.springframework.stereotype.Component;

/**
 * Upload an appspec Service
 * 
 * On this page, the user may upload an appspec.
 * 
 * Automatically generated by clickframes
 */
@Component("uploadService")
public class UploadService {
    private final Log logger = LogFactory.getLog(getClass());

    /**
     * Upload
     * 
     * @param file
     * @param app
     * @throws EndUserException
     * 
     */
    // Automatically generated by clickframes
    public UploadFormUploadResponse uploadFormUpload(File file) {
        try {
            Appspec appspec = org.clickframes.AppspecReader.readProject(new FileInputStream(file));

            App app = new App();

            String appId = App.getNewAppId(appspec);
            app.setId(appId);

            // save appspec
            File appDir = new File(App.getAppsDir(), appId);
            appDir.mkdirs();

            File destinationFile = new File(appDir, "appspec.xml");
            FileUtils.copyFile(file, destinationFile);

            // create a techspec
            Techspec techspec = new Techspec(null);
            Plugin plugin = new Plugin();
            plugin.setClazz("clips");
            techspec.getPlugins().add(plugin);
            techspec.setOutputDirectory(appDir);

            // List<String> autoscanTemplates =
            // RuntimeManifestGenerator.getAllTemplatesInWarContext();
            List<String> autoscanTemplates = getAllTemplatesInWarContext();

            logger.info("Generating project artifacts...");
            TechspecRunner.run(techspec, appspec, autoscanTemplates);

            return new UploadFormUploadResponse(UploadUploadFormUploadOutcome.appspecIsValid, null, app);
        } catch (FileNotFoundException e) {
            throw new RuntimeException("The file passed to create project does not exist", e);
        } catch (JAXBException e) {
            return new UploadFormUploadResponse(UploadUploadFormUploadOutcome.appspecIsInvalid, e.getMessage(), null);
            // throw new EndUserException(
            // "Invalid appspec xml, does not conform to schema", e);
        } catch (InternalLinkResolutionException e) {
            return new UploadFormUploadResponse(UploadUploadFormUploadOutcome.appspecIsInvalid, e.getMessage(), null);
            // throw new EndUserException(
            // "Invalid appspec xml, one of the internal page references does not resolve to a valid page"
            // ,
            // e);
        } catch (PageNotFoundException e) {
            return new UploadFormUploadResponse(UploadUploadFormUploadOutcome.appspecIsInvalid, e.getMessage(), null);
            // throw new EndUserException(
            // "Invalid appspec xml, default page or one of the internal page references does not resolve to a valid page"
            // ,
            // e);
        } catch (AppspecConstraintViolationException e) {
            return new UploadFormUploadResponse(UploadUploadFormUploadOutcome.appspecIsInvalid, e.getMessage(), null);
            // throw new EndUserException(e.getMessage(), e);
        } catch (IOException e) {
            throw new RuntimeException("Input/output error", e);
        }
    }

    private List<String> getAllTemplatesInWarContext() throws IOException {
        List<String> retVal = new ArrayList<String>();

        URL[] urls = getURLs();

        for (URL url : urls) {
            // each URL is a jar file
            // Get the jar file
            if (url.getProtocol().equals("file")) {
                String path = url.getPath();
                File file = new File(path);

                // for jar files
                if (!file.isDirectory() && file.exists()) {
                    retVal.addAll(RuntimeManifestGenerator.getAutoscanTemplatesFromJar(new JarFile(file)));
                }
            }
        }

        return retVal;
    }

    private URL[] getURLs() {
        // return getURLsCompileTimeLinked();
        return getURLsDynamicTimeLinked();
    }

    private URL[] getURLsCompileTimeLinked() {
        // ClassLoader currentLoader = this.getClass().getClassLoader();
        //
        // Class<? extends ClassLoader> clazz = currentLoader.getClass();
        // if
        // (clazz.getName().matches("org.jboss.web.tomcat.service.WebAppClassLoader"))
        // {
        // org.apache.catalina.loader.WebappClassLoader webappClassLoader =
        // (org.apache.catalina.loader.WebappClassLoader) currentLoader;
        //
        // return webappClassLoader.getURLs();
        // }
        //
        return new URL[] {};
    }

    private URL[] getURLsDynamicTimeLinked() {
        ClassLoader currentLoader = this.getClass().getClassLoader();

        try {
            // invoke the getURLs method on the currentLoader, if that method
            // exists
            Class<? extends ClassLoader> clazz = currentLoader.getClass();
            Method getURLs = clazz.getMethod("getURLs");
            if (getURLs != null) {
                Object result = getURLs.invoke(currentLoader);

                URL[] urls = (URL[]) result;

                return urls;
            }
        } catch (Throwable t) {
            logger.error("Error while getting urls from classpath", t);
            return new URL[] {};
        }
        return new URL[] {};
    }

    public static class UploadFormUploadResponse {
        private UploadUploadFormUploadOutcome outcome;

        private App app;

        private String errorMessage;

        public String getErrorMessage() {
            return errorMessage;
        }

        public UploadFormUploadResponse(UploadUploadFormUploadOutcome outcome, String errorMessage, App app) {
            this.outcome = outcome;
            this.errorMessage = errorMessage;
            this.app = app;
        }

        public UploadUploadFormUploadOutcome getOutcome() {
            return outcome;
        }

        public App getApp() {
            return app;
        }

        public void setApp(App app) {
            this.app = app;
        }
    }
}
// clickframes::version=3595117483::clickframes
